package com.gitee.wsl.ext.collection



/**
 * Find the first element in iterable that is an instance of the specified class.
 *
 * @param T the type of the class
 * @return the first element that is an instance of the specified class
 * @throws NoSuchElementException if no such element is found
 */
inline fun <reified T> Iterable<*>.firstInstance() = first { it is T } as T

/**
 * Find the first element in iterable that is an instance of the specified class or null if no such element is found.
 *
 * @param T the type of the class
 * @return the first element that is an instance of the specified class or null if no such element is found
 * @throws NoSuchElementException if no such element is found
 */
inline fun <reified T> Iterable<*>.firstInstanceOrNull() = firstOrNull { it is T } as? T


//inline fun <reified R> Iterable<*>.firstInstanceOfOrNull(): R? {
//    val iterator = iterator()
//    while (iterator.hasNext()) {
//        val element = iterator.next()
//
//        if (element is R)
//            return element
//    }
//
//    return null
//}
//
//inline fun <reified R> Iterable<*>.firstInstanceOf(): R {
//    return firstInstanceOfOrNull()
//        ?: throw NoSuchElementException("No element of type ${R::class} found")
//}


inline fun <reified R> Iterable<*>.filterIsInstance(predicate: (R) -> Boolean): List<R> =
    filterIsInstanceTo(mutableListOf(), predicate)

inline fun <reified R, C : MutableCollection<in R>> Iterable<*>.filterIsInstanceTo(
    destination: C,
    predicate: (R) -> Boolean,
): C {
    for (element in this) if (element is R && predicate(element)) destination.add(element)
    return destination
}


inline fun <reified R> Iterable<Any>.findInstance(predicate: (R) -> Boolean = { true }): R? {
    return find { it is R && predicate(it) } as R?
}


inline fun <reified T : Any> Iterable<*>.lastIsInstanceOrNull(): T? {
    when (this) {
        is List<*> -> {
            for (i in this.indices.reversed()) {
                val element = this[i]
                if (element is T) return element
            }
            return null
        }

        else -> {
            return reversed().firstInstanceOrNull<T>()
        }
    }
}
